/**
 * Wihidum - Efficient Broker Hierarchy for Publish Subscribe Middleware
 *
 * License      :   Apache License 2.0
 * Home Page    :   http://code.google.com/p/wihidum/
 * Mailing List :   wihidum@googlegroups.com
 */

package lk.mrt.cse.wihidum.processor;

import org.apache.axiom.soap.*;
import org.apache.axiom.om.*;
import org.apache.log4j.Logger;
import org.apache.axis2.client.ServiceClient;
import org.apache.axis2.client.Options;
import org.apache.axis2.client.OperationClient;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.AxisFault;
import org.apache.axis2.wsdl.WSDLConstants;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.httpclient.HttpClient;

import javax.xml.namespace.QName;

import lk.mrt.cse.wihidum.util.WihidumConstants;
import lk.mrt.cse.wihidum.util.WSEventingConstants;
import lk.mrt.cse.wihidum.storage.BrokerList;
import lk.mrt.cse.wihidum.storage.Broker;

import java.util.ArrayList;

public class ChangeTopologyProcessor implements Processor{

    private static final Logger logger = Logger.getLogger(ChangeTopologyProcessor.class);
    private HttpClient httpClient;

    public ChangeTopologyProcessor()
    {
        httpClient = new HttpClient();    
    }

    public SOAPEnvelope processMessage(SOAPEnvelope requestEnvelope) {
        try {
            SOAPFactory factory = OMAbstractFactory.getSOAP11Factory();
            SOAPEnvelope responseEnvelop = factory.getDefaultEnvelope();

            OMElement topologyElement = requestEnvelope.getBody()
                    .getFirstChildWithName(new QName("TopologyDetails"))
                    .getFirstChildWithName(new QName("Topology"));

            OMElement responseElement = factory.createOMElement("Response", null);
            if(this.distributeTopologyChangeMessage(topologyElement.getText()))
            {
                responseElement.setText("Topology changed to " +topologyElement.getText() + " successfully");
                logger.info("Topology changed to " +topologyElement.getText() + " successfully");
            }
            else
            {
                responseElement.setText("Topology changed to " +topologyElement.getText() + " was unsuccessful");
            }

            responseEnvelop.getBody().addChild(responseElement);            

            return responseEnvelop;
        }
        catch (NullPointerException ex)
        {
            logger.error("Error in processing received change topology message", ex);
            return null;
        }
        catch (OMException ex)
        {
            logger.error("Error in processing received change topology message", ex);
            return null;
        }
    }

    private Boolean distributeTopologyChangeMessage(String topology)
    {
        Boolean success = false;
        ServiceClient serviceClient = null;
        try
        {
            if(!BrokerList.isEmpty())
            {
                serviceClient = new ServiceClient();
                ArrayList<Broker> brokerList = BrokerList.getBrokerList();
                for(Broker broker: brokerList)
                {
                    OperationClient operationClient = serviceClient.createClient(ServiceClient.ANON_OUT_IN_OP);

                    //creating message context
                    MessageContext outMessageContext = new MessageContext();
                    Options options = outMessageContext.getOptions();

                    options.setProperty(HTTPConstants.CACHED_HTTP_CLIENT, httpClient);
                    options.setProperty(HTTPConstants.REUSE_HTTP_CLIENT, Boolean.TRUE);

                    String SOAPVersion = options.getSoapVersionURI();
                    if (SOAPVersion == null)
                        SOAPVersion = SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI;

                    EndpointReference targetEPR = new EndpointReference(broker.getAddress());
                    options.setTo(targetEPR);
                    options.setAction(WihidumConstants.Operations.CHANGE_TOPOLOGY);

                    SOAPEnvelope changeTopologyEnvelope = this.createTopologyChangeMessage(SOAPVersion, topology);

                    outMessageContext.setEnvelope(changeTopologyEnvelope);
                    operationClient.addMessageContext(outMessageContext);
                    operationClient.execute(true);
                    MessageContext inMsgtCtx = operationClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);

                    SOAPEnvelope response = inMsgtCtx.getEnvelope();
                    OMElement responseElement = response.getBody().getFirstElement();
                     if(responseElement.getText().equals("Topology changed to " +topology+ " successfully"))
                     {
                        logger.info("Change topology completed for broker: "+broker.getAddress()+" successfully");
                        success = true;
                     }
                }
            }
            else
            {
                success = false;
            }
        } catch (AxisFault axisFault)
        {
            axisFault.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            success = false;
        }
        finally {
            return success;
        }

    }
    
    private SOAPEnvelope createTopologyChangeMessage(String SOAPURI, String topology)
    {
       try {
           SOAPFactory soapFactory = null;
           if (SOAP11Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
               soapFactory = OMAbstractFactory.getSOAP11Factory();
           else if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI.equals(SOAPURI))
               soapFactory = OMAbstractFactory.getSOAP12Factory();
           else throw new Exception("Unknown SOAP version");

           SOAPEnvelope soapEnvelope = soapFactory.getDefaultEnvelope();

           //declare the used namespaces in the SOAP Envelope
           OMNamespace wseventingNS = soapFactory.createOMNamespace(WSEventingConstants.EVENTING_NAMESPACE, WSEventingConstants.EVENTING_PREFIX);
           soapEnvelope.declareNamespace(wseventingNS);
           OMNamespace wsaNS = soapFactory.createOMNamespace(WSEventingConstants.ADDRESSING_NAMESPACE, WSEventingConstants.ADDRESSING_PREFIX);
           soapEnvelope.declareNamespace(wsaNS);
           OMNamespace wihidumNS = soapFactory.createOMNamespace(WSEventingConstants.WIHIDUM_NAMESPACE, WSEventingConstants.WHIDUM_NAMESPACE_PREFIX);
           soapEnvelope.declareNamespace(wihidumNS);

           OMFactory factory = OMAbstractFactory.getOMFactory();

           OMElement messageElement = factory.createOMElement("TopologyDetails", wihidumNS);
           OMElement nameElement = factory.createOMElement("Topology", wihidumNS);
           nameElement.setText(topology);
           messageElement.addChild(nameElement);

           SOAPBody soapBody = soapEnvelope.getBody();
           soapBody.addChild(messageElement);

           return soapEnvelope;
        }
        catch (NullPointerException nullEx)
        {
            logger.error("Error in processing Change Topology response. " + nullEx);
            return null;
        }
        catch (AxisFault axisFault)
        {
            logger.error("Error in sending Change Topology request. " + axisFault);
            return null;
        } catch (Exception e)
        {
            logger.error(e.getMessage());
            return null;
        }
    }
}

